﻿/*
 * @author: wizardc
 */

using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

namespace DouEditor.Extend
{
    public sealed class ParticleEditorPlayMgr
    {
        private static ParticleEditorPlayMgr _instance;
        public static ParticleEditorPlayMgr instance => _instance ??= new ParticleEditorPlayMgr();

        private Dictionary<ParticleSystem, ParticleEditorPlayInfo> _map = new Dictionary<ParticleSystem, ParticleEditorPlayInfo>();
        private double _prevTime;

        private ParticleEditorPlayMgr()
        {
            _prevTime = EditorApplication.timeSinceStartup;

            EditorApplication.update -= OnEditorUpdate;
            EditorApplication.update += OnEditorUpdate;
        }

        public ParticleEditorPlayInfo AddData(ParticleSystem particleSystem)
        {
            RemoveData(particleSystem);
            var info = new ParticleEditorPlayInfo(particleSystem);
            _map.Add(particleSystem, info);
            return info;
        }

        public ParticleEditorPlayInfo Get(ParticleSystem particleSystem)
        {
            if (_map.ContainsKey(particleSystem))
            {
                return _map[particleSystem];
            }
            return null;
        }

        public void RemoveData(ParticleSystem particleSystem)
        {
            if (_map.ContainsKey(particleSystem))
            {
                var info = _map[particleSystem];
                info.Destroy();
                _map.Remove(particleSystem);
            }
        }

        private void OnEditorUpdate()
        {
            var now = EditorApplication.timeSinceStartup;
            var passedTime = now - _prevTime;
            _prevTime = now;
            foreach (var kv in _map)
            {
                kv.Value.Update((float) passedTime);
            }
        }
    }

    public class ParticleEditorPlayInfo
    {
        private ParticleSystem _particleSystem;

        private bool _withChildren = true;
        private bool _playing;
        private float _currentTime;
        private float _totalTime;
        private bool _isLoop = false;
        private bool _timeGoesOn = false;
        private float _ratio = 1;

        public ParticleSystem particleSystem => _particleSystem;

        /// <summary>
        /// 是否同时控制所有子项粒子动画
        /// </summary>
        public bool withChildren
        {
            set => _withChildren = value;
            get => _withChildren;
        }

        public bool playing => _playing;

        public float currentTime
        {
            set
            {
                _currentTime = value;
                Reset(_currentTime);
            }
            get => _currentTime;
        }

        public float totalTime => _totalTime;

        public bool isLoop
        {
            set => _isLoop = value;
            get => _isLoop;
        }

        /// <summary>
        /// 非循环模式下，时间到达后是否继续计时
        /// </summary>
        public bool timeGoesOn
        {
            set => _timeGoesOn = value;
            get => _timeGoesOn;
        }

        public float ratio
        {
            set => _ratio = value;
            get => _ratio;
        }

        public ParticleEditorPlayInfo(ParticleSystem particleSystem)
        {
            _particleSystem = particleSystem;
            _totalTime = _particleSystem.main.duration;
        }

        public void Update(float passTime)
        {
            if (_playing)
            {
                var pass = passTime * _ratio;
                _currentTime += pass;
                _particleSystem.Simulate(pass, _withChildren, false);
                if (_currentTime >= _totalTime)
                {
                    if (_isLoop)
                    {
                        _currentTime = _currentTime % _totalTime;
                        Reset(_currentTime);
                    }
                    else if (!_timeGoesOn)
                    {
                        _currentTime = _totalTime;
                    }
                }
            }
        }

        public void Play()
        {
            Stop();
            _playing = true;
            _currentTime = 0;
            Reset();
        }

        /// <summary>
        /// 重置粒子系统到初始状态
        /// 要正常使用 Simulate 方法，必须先设置时间为 0 同时 restart 为 true，然后 restart 设置为 false ，通过 deltaTime 增加间隔时间来播放
        /// 如果每帧都基于当前时间调用 restart 为 true 时，会重复发射粒子出现异常
        /// </summary>
        private void Reset(float time = 0)
        {
            _particleSystem.Simulate(time, _withChildren, true);
        }

        public void Pause()
        {
            _playing = false;
        }

        public void Resume()
        {
            _playing = true;
        }

        public void Stop()
        {
            _playing = false;
            _currentTime = 0;
            Reset();
        }

        public void Destroy()
        {
            _particleSystem.Clear();
        }
    }
}
